/*******************************************************************
 * File:     interact
 * Purpose:  Interactive interface
 * Author:   Justin Fletcher
 * Date:     11 Jan 2006
 * License:  Free for any use with no restrictions or requirements
 ******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "interact.h"

interactlink_t *__headinteract = NULL;

/*************************************************** Gerph *********
 Function:     interact_command
 Description:  Process a command for our interaction interface
 Parameters:   cli-> the command line to process
               invalid_terminates = the return code for an invalid
                                    command
 Returns:      return code for the command - non-zero will terminate
               the interaction shell, remember
 ******************************************************************/
int interact_command(char *cli, int invalid_rc)
{
  char *end;
  char *tail;
  char *command;
  command = cli;

  /* Skip any spaces */
  while (*cli==' ')
    cli++;

  /* Any null command might as well return 0 */
  if (*cli=='\0')
    return 0;

  end = strchr(cli, ' ');
  if (end)
  {
    *end = '\0';
    tail = end+1;
  }
  else
    tail = ""; /* No tail present */

  /* Skip any spaces */
  while (*tail==' ')
    tail++;

  /* Find the command from the linked list */
  {
    interactlink_t *il;
    for (il = __headinteract;
         il;
         il=il->next)
    {
      /* For each linked entry, process the array of commands */
      interactcommand_t *com;
      for (com=il->commands;
           com && com->command != NULL;
           com++)
      {
        if (strcmp(command, com->command)==0)
        {
          /* Found the command so call the processor, and return
             the return code back to the caller. */
          return com->process_func(tail);
        }
      }
    }
  }

  /* If we reach here, the command has not been recognised */
  printf("Command '%s' not understood\n", command);
  return invalid_rc;
}

/*************************************************** Gerph *********
 Function:     interact_shell
 Description:  Run the interaction interface
 Parameters:   prompt-> the prompt to give
 Returns:      value returned from final interaction command,
               or 0 if the last command returned no error
 ******************************************************************/
int interact_shell(const char *prompt)
{
  int rc = 0;
  char cli[256];

  while (1)
  {
    printf("%s> ", prompt);
    if (fgets(cli, sizeof(cli), stdin))
    {
      char *end= strchr(cli, '\n');
      if (end) *end = '\0';

      /* Process the command, but invalid commands return 0 so our
         shell keeps running */
      rc =interact_command(cli, 0);
      if (rc)
        break;
    }
  }

  return rc;
}


/******************************************************************/

/* Now the actual processing commands that we provide ourselves.
   If you want to provide your own interactive commands in another
   source file, you would provide the functions for them as
   below and supply a command table and link entry at the end of
   the file.
 */

/*************************************************** Gerph *********
 Function:     interact_quit
 Description:  Quit the shell - we just return 1 to cause the shell
               to terminate
 Parameters:   tail-> any tail on the command
 Returns:      1
 ******************************************************************/
static int interact_quit(char *tail)
{
  return 1;
}

/*************************************************** Gerph *********
 Function:     interact_help
 Description:  Display help
 Parameters:   tail-> any tail on the command
 Returns:      0
 ******************************************************************/
static int interact_help(char *tail)
{
  /* The order of the help output will be sort-of undefined.
     Each linker-set will have its commands together. We could
     exploit this by including a module name with each group
     and highlighting them in that way, but this is overkill for
     our purposes. */
  interactlink_t *il;
  for (il = __headinteract;
       il;
       il=il->next)
  {
    /* For each linked entry, process the array of commands */
    interactcommand_t *com;
    for (com=il->commands;
         com && com->command != NULL;
         com++)
    {
      printf("%-16s %s\n", com->command, com->help);
    }
  }

  return 0;
}

/* This is the list of the commands we provide. It is referenced
   from the linker set below. The list is terminated by a NULL command
   pointer. */
static interactcommand_t commands[] = {
  { "help", "Display list of commands", interact_help },
  { "quit", "Exit the shell",           interact_quit },
  { NULL }
};

/* This is how we generate the linker-set.
   Each __linkinteract will be chained with the others to form
   a list of which __headinteract is the first reference. The
   first word of this structure must always be zero. We could
   have any number of other members of the structure, but we
   only need to have a list of commands we provide. */
static interactlink_t __linkinteract = {
  NULL, commands
};
